﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using l1cs.server.server.utils;
using l1cs.server.model.Instance;
using System.Xml;
using System.Collections;
using l1cs.server.model.npc.action;
using l1cs.server.datatables;
using l1cs.server.serverpackets;

namespace l1cs.server.model
{
    public class L1TreasureBox
    {
        private static Logger _log = Logger.getLogger( typeof( L1TreasureBox ).FullName );

        private const String PATH = "./data/xml/Item/TreasureBox.xml";

        private static Dictionary<int, TreasureBox> _dataMap = new Dictionary<int, TreasureBox>();

        public const int RANDOM = 0;

        public const int SPECIFIC = 1;

        /**
         * 指定されたIDのTreasureBoxを返す。
         * 
         * @param id - TreasureBoxのID。普通はアイテムのItemIdになる。
         * @return 指定されたIDのTreasureBox。見つからなかった場合はnull。
         */
        public static TreasureBox get( int id )
        {
            return _dataMap[id];
        }

        private L1TreasureBox()
        {
            PerformanceTimer timer = new PerformanceTimer();
            _log.info( "loading TreasureBox..." );

            // 「XmlReader」インスタンスの宣言(nullで初期化)
            XmlReader reader = null;
            // 特別なルールを設定する場合、「XmlReaderSettings」クラスを使用する。
            XmlReaderSettings settings = new XmlReaderSettings();
            // 検証は「整形式かどうか」の検査のみ
            settings.ConformanceLevel = ConformanceLevel.Document;

            // 「XmlReader」の作成
            reader = XmlReader.Create( PATH, settings );

            TreasureBox box = null;
            while ( reader.Read() )
            {
                // 開始タグを発見した場合
                if ( reader.NodeType == XmlNodeType.Element )
                {
                    if ( string.Compare( reader.LocalName, "TreasureBox", true ) == 0 )
                    {
                        Hashtable attribute = L1NpcXmlParser.getAllAttribute( reader );
                        string sType = (string)attribute["Type"];
                        int type = 0;
                        if ( String.Compare( sType, "RANDOM", true ) == 0 )
                        {
                            type = RANDOM;
                        }
                        else if ( String.Compare( sType, "SPECIFIC", true ) == 0 )
                        {
                            type = SPECIFIC;
                        }
                        int boxId = Convert.ToInt32( attribute["ItemId"] );
                        box = new TreasureBox( type, boxId );
                    }
                    else if ( string.Compare( reader.LocalName, "Item", true ) == 0 )
                    {
                        Hashtable attribute = L1NpcXmlParser.getAllAttribute( reader );
                        int itemId = Convert.ToInt32( attribute["ItemId"] );
                        int count = Convert.ToInt32( attribute["Count"] );
                        double chance = Convert.ToDouble( attribute["Chance"] );
                        box.addItem( new Item( itemId, count, chance ) );
                    }
                }
                else if ( reader.NodeType == XmlNodeType.EndElement &&
                          string.Compare( reader.LocalName, "TreasureBox", true ) == 0 )
                {
                    foreach ( Item each in box.getItems() )
                    {
                        box.addTotalChance( each.getChance() );
                        if ( ItemTable.getInstance().getTemplate( each.getItemId() ) == null )
                        {
                            box.removeItem( each );
                            _log.warning( "アイテムID " + each.getItemId() + " のテンプレートが見つかりません。" );
                        }
                    }
                    if ( box.getTotalChance() != 0 && box.getTotalChance() != 1000000 )
                    {
                        _log.warning( "ID " + box.getBoxId() + " の確率の合計が100%になりません。" );
                    }
                    if ( _dataMap.ContainsKey( box.getBoxId() ) )
                    {
                        _log.info( "Itemid=" + box.getBoxId() + "が重複しています。" );
                    }
                    else
                    {
                        _dataMap.Add( box.getBoxId(), box );
                    }
                }
            }
            _log.info( "OK! " + timer.get() + "ms" );
        }

        private static L1TreasureBox _instance;

        public static L1TreasureBox getInstance()
        {
            if ( _instance == null )
            {
                _instance = new L1TreasureBox();
            }
            return _instance;
        }
    }

    public class TreasureBox
    {
        private int _boxId;

        private int _type;

        private int _totalChance = 0;

        private List<Item> items = new List<Item>();

        public TreasureBox( int type, int boxId )
        {
            _boxId = boxId;
            _type = type;
        }

        public int getBoxId()
        {
            return _boxId;
        }

        public Item getItem( int i )
        {
            return items[i];
        }

        public List<Item> getItems()
        {
            return items;
        }

        public void addItem( Item item )
        {
            items.Add( item );
        }

        public void removeItem( Item item )
        {
            items.Remove( item );
        }

        public void addTotalChance( int totalChance )
        {
            _totalChance += totalChance;
        }

        public int getTotalChance()
        {
            return _totalChance;
        }

        /**
         * TreasureBoxを開けるPCにアイテムを入手させる。PCがアイテムを持ちきれなかった場合は
         * アイテムは地面に落ちる。
         * 
         * @param pc - TreasureBoxを開けるPC
         * @return 開封した結果何らかのアイテムが出てきた場合はtrueを返す。
         *         持ちきれず地面に落ちた場合もtrueになる。
         */
        public bool open( L1PcInstance pc )
        {
            L1ItemInstance item = null;

            if ( _type == L1TreasureBox.SPECIFIC )
            {
                // 出るアイテムが決まっているもの
                foreach ( Item each in items )
                {
                    item = ItemTable.getInstance().createItem( each.getItemId() );
                    if ( item != null )
                    {
                        item.setCount( each.getCount() );
                        storeItem( pc, item );
                    }
                }

            }
            else if ( _type == L1TreasureBox.RANDOM )
            {
                // 出るアイテムがランダムに決まるもの
                Random random = new Random();
                int chance = 0;

                int r = random.Next( _totalChance );

                foreach ( Item each in items )
                {
                    chance += each.getChance();

                    if ( r < chance )
                    {
                        item = ItemTable.getInstance().createItem( each.getItemId() );
                        if ( item != null )
                        {
                            item.setCount( each.getCount() );
                            storeItem( pc, item );
                        }
                        break;
                    }
                }
            }

            if ( item == null )
            {
                return false;
            }
            else
            {
                int itemId = getBoxId();

                // 魂の結晶の破片、魔族のスクロール、ブラックエントの実
                if ( itemId == 40576 || itemId == 40577 || itemId == 40578
                        || itemId == 40411 || itemId == 49013 )
                {
                    pc.death( null ); // キャラクターを死亡させる
                }
                return true;
            }
        }

        private void storeItem( L1PcInstance pc, L1ItemInstance item )
        {
            L1Inventory inventory;

            if ( pc.getInventory().checkAddItem( item, item.getCount() ) == L1Inventory.OK )
            {
                inventory = pc.getInventory();
            }
            else
            {
                // 持てない場合は地面に落とす 処理のキャンセルはしない（不正防止）
                inventory = L1World.getInstance().getInventory( pc.getLocation() );
            }
            inventory.storeItem( item );
            pc.sendPackets( new S_ServerMessage( 403, item.getLogName() ) ); // %0を手に入れました。
        }
    }

    public class Item
    {
        private int _itemId;

        private int _count;

        private int _chance;

        public Item( int itemId, int count, double chance )
        {
            _itemId = itemId;
            _count = count;
            _chance = (int)( chance * 10000 );
        }

        public int getItemId()
        {
            return _itemId;
        }

        public int getCount()
        {
            return _count;
        }

        public int getChance()
        {
            return _chance;
        }
    }
}
